/*
 * PCL.cpp
 *
 *  Created on: 2012. 10. 15.
 *      Author: dalxx
 */

//#include <Arduino.h>
//#include <pins_arduino.h>
#include "PLC.h"
#include "PLCLib/Relay.h"
#include "PLCLib/AnalogInputRelay.h"
#include "PLCLib/DigitalInputRelay.h"
#include "PLCLib/DigitalLogicRelay.h"
#include "PLCLib/TimerRelay.h"
#include "PLCLib/Output.h"

#include "PLCLib/BooleanCalculator.h"

#include "PLCLib/ParseTool.h"
#include "StringStream.h"



const char* PLC::CMD_PLC_SET="PLC_SET";
const char* PLC::CMD_STOP="PLC_STOP";
const char* PLC::CMD_START="PLC_START";
const char* PLC::CMD_CHANG_MOD="PLC_CHANGE_MOD";

const char* PLC::OUT_OK="OK";
const char* PLC::ERR_FMT_PLC_SET="PLC_SET_READ_ERROR";

const char* VREF_DEFAULT="DEFAULT";
const char* VREF_INTERNAL="INTERNAL";
const char* VREF_EXTERNAL="EXTERNAL";

PLC_CMD_HANDLER Handler[]={{&PLC::createRelay,PLC::CMD_PLC_SET}};

PLC::PLC(Stream& _cmdReader) {
	 this->nRelay=0;
	 this->relay_list=NULL;
	 this->nOutput=0;
	 this->output=NULL;
	 this->cmdConsole=&_cmdReader;
#ifdef  SERIAL_TRACE_ON
	Serial.println("PLC start");

#endif
}

PLC::~PLC() {
	this->deleteAll();
}

void PLC::deleteAll() {
	int i;
	if(this->nRelay!=0)
	{
		for(i=0;i<this->nRelay;i++)
		{
			delete this->relay_list[i];

		}

		free(this->relay_list);
		this->relay_list=NULL;
		this->nRelay=0;
	}
	if(this->nOutput!=0)
	{
		for(i=0;i<this->nOutput;i++)
		{
			delete this->output[i];

		}
		free(this->output);
		this->nOutput=0;
	}
}

void PLC::doPLC() {
	this->processCommand();
	if(this->nOutput==0)
	{
		return;
	}
	this->calcuateRelay();
}

void PLC::calcuateRelay() {
	int i;
	for(i=0;i<this->nRelay;i++)
	{
		this->relay_list[i]->CalculateNext();
	}
	for(i=0;i<this->nRelay;i++)
	{
		this->relay_list[i]->Commit();
	}
	for(i=0;i<this->nOutput;i++)
	{
		this->output[i]->Out();
	}


}

/*readRelay
 * create from cmdStream
 */
void PLC::createRelay() {
	//int size;
	float Vref;
	int nDI, nDL, nAI, nT, nOut, refType=DEFAULT,i=0, relayIndex=0;
	char* buffer=this->full_buffer;
	int str_size, bsize=BUFFER_SIZE;
	bool stat=true;
	char tmpName[NAME_SIZE];
	StringStream bufStream;
	//read header

	{

		str_size=PLCLib::ParseTool::ReadString(buffer, bsize, this->cmdConsole, ';',';');if(str_size==0) goto exception;
		bufStream.resetBuffer(buffer,str_size);

#ifdef  SERIAL_TRACE_ON
		Serial.print("Header: "); Serial.print(buffer);
		Serial.println();
#endif

		stat&=PLCLib::ParseTool::ReadInt(nDI,&bufStream, ',','\0');
		stat&=PLCLib::ParseTool::ReadInt(nDL, &bufStream, ',','\0');
		stat&=PLCLib::ParseTool::ReadInt(nAI, &bufStream, ',','\0');
		stat&=PLCLib::ParseTool::ReadInt(nT, &bufStream, ',','\0');
		stat&=PLCLib::ParseTool::ReadInt(nOut, &bufStream, ',','\0');

		if(nAI!=0)
		{
			if(PLCLib::ParseTool::ReadString(buffer, str_size, &bufStream, ',','\0')==0) goto exception;
			if(strcmp(buffer, VREF_DEFAULT)!=0)
				refType=DEFAULT;
			else if(strcmp(buffer, VREF_INTERNAL)!=0)
				refType=INTERNAL;
			else if(strcmp(buffer, VREF_EXTERNAL)!=0)
				refType=EXTERNAL;
			analogReference(refType);
			stat&=PLCLib::ParseTool::ReadFloat(Vref, &bufStream, '\0','\0');
		}
		else
		{
			stat&=PLCLib::ParseTool::FindUntil(&bufStream,'\0','\0');
		}
		if(!stat) goto exception;

		this->nRelay=nDI+nDL+nAI+nT;
		this->relay_list=(PLCLib::Relay**)malloc(this->nRelay*sizeof(PLCLib::Relay*));

		for(i=0;i<this->nRelay;i++)
		{
			this->relay_list[i]=NULL;
		}
		this->nOutput=nOut;
		this->output=(PLCLib::Output**)malloc(this->nOutput*sizeof(PLCLib::Output*));
		for(i=0;i<this->nOutput;i++)
		{
			this->output[i]=NULL;
		}
#ifdef  SERIAL_TRACE_ON
		Serial.print("nDI: ");	Serial.print(nDI);
		Serial.print(", nDL: "); Serial.print(nDL);
		Serial.print(", nAI: ");Serial.print(nAI);
		Serial.print(", nT: ");	Serial.print(nT);
		Serial.print(", nOut: "); Serial.print(nOut);
		Serial.print(", refType: "); Serial.print(refType);
		Serial.print(", refV: "); Serial.print(Vref);
		Serial.println();
#endif
		for(i=0;i<nDI;i++,relayIndex++)
		{
			int di_port;
			str_size=PLCLib::ParseTool::ReadString(buffer, bsize, this->cmdConsole, ';',';');if(str_size==0) goto exception;
			bufStream.resetBuffer(buffer,str_size);
#ifdef  SERIAL_TRACE_ON
			Serial.print("DI line: "); Serial.print(buffer);
			Serial.println();
#endif
			if(PLCLib::ParseTool::ReadString(tmpName, NAME_SIZE, &bufStream, ',','\0')==0) goto exception;
			stat&=PLCLib::ParseTool::ReadInt(di_port, &bufStream, '\0','\0');
			if(!stat) goto exception;
			this->relay_list[relayIndex]=new PLCLib::DigitalInputRelay(tmpName, di_port);
#ifdef  SERIAL_TRACE_ON
			Serial.print("DI name: ");		Serial.print(tmpName);
			Serial.print(", port: "); Serial.print(di_port);
			Serial.println();
#endif
		}

		char* logics[nDL];

		for(i=0;i<nDL;i++,relayIndex++)
		{
			int expressionSize=0;

			str_size=PLCLib::ParseTool::ReadString(buffer, bsize, this->cmdConsole, ';',';');if(str_size==0) goto exception;
			bufStream.resetBuffer(buffer,str_size);

			if(PLCLib::ParseTool::ReadString(tmpName, NAME_SIZE, &bufStream, ',','\0')==0) goto exception;

			expressionSize=PLCLib::ParseTool::ReadString(buffer, str_size, &bufStream, '\0','\0');
			if(expressionSize==0) goto exception;
			this->relay_list[relayIndex]=new PLCLib::DigitalLogicRelay(tmpName);
			logics[i]=buffer;
			buffer+=expressionSize;
			bsize-=expressionSize;
#ifdef  SERIAL_TRACE_ON
			Serial.print("DL name : ");Serial.print(tmpName);
			Serial.print(", buffer: "); Serial.print((int)buffer);
			Serial.print(", logic: "); Serial.print(logics[i]);
			Serial.println();
#endif
		}

		for(i=0;i<nAI;i++,relayIndex++)
		{
			int ai_port, rangeMin, rangeMax;
			bool type;
			char typeString[5];
			str_size=PLCLib::ParseTool::ReadString(buffer, bsize, this->cmdConsole, ';',';');if(str_size==0) goto exception;
			bufStream.resetBuffer(buffer,str_size);

			if(PLCLib::ParseTool::ReadString(tmpName, NAME_SIZE, &bufStream, ',','\0')==0) goto exception;
			stat&=PLCLib::ParseTool::ReadInt(ai_port, &bufStream, ',','\0');if(!stat) goto exception;

			if(PLCLib::ParseTool::ReadString(typeString, 5, &bufStream, ',','\0')==0) goto exception;
			if(typeString[0]=='T' or typeString[0]=='t') type=true;
			else type=false;

			stat&=PLCLib::ParseTool::ReadInt(rangeMin, &bufStream, ',','\0');if(!stat) goto exception;
			stat&=PLCLib::ParseTool::ReadInt(rangeMax, &bufStream, ',','\0');if(!stat) goto exception;
			if(!stat) goto exception;

			this->relay_list[relayIndex]=new PLCLib::AnalogInputRelay( tmpName, ai_port+A0, Vref, rangeMin, rangeMax, type);
		}

		for(i=0;i<nT;i++,relayIndex++)
		{
			int delay, duration;

			str_size=PLCLib::ParseTool::ReadString(buffer, bsize, this->cmdConsole, ';',';');if(str_size==0) goto exception;
			bufStream.resetBuffer(buffer,str_size);

			if(PLCLib::ParseTool::ReadString(tmpName, NAME_SIZE, &bufStream, ',','\0')==0) goto exception;
			stat&=PLCLib::ParseTool::ReadInt(delay, &bufStream, ',','\0');if(!stat) goto exception;
			stat&=PLCLib::ParseTool::ReadInt(duration, &bufStream, '\0','\0');if(!stat) goto exception;

			if(!stat) goto exception;
			this->relay_list[relayIndex]=new PLCLib::TimerRelay( tmpName, delay, duration);
		}

		for(i=0;i<nOut;i++)
		{
			int outport ;

			str_size=PLCLib::ParseTool::ReadString(buffer, bsize, this->cmdConsole, ';',';');if(str_size==0) goto exception;
			bufStream.resetBuffer(buffer,str_size);

			if(PLCLib::ParseTool::ReadString(tmpName, NAME_SIZE, &bufStream, ',','\0')==0) goto exception;
			stat&=PLCLib::ParseTool::ReadInt(outport, &bufStream, ',','\0');if(!stat) goto exception;
			if(PLCLib::ParseTool::ReadString(buffer, NAME_SIZE, &bufStream, ',','\0')==0) goto exception;

			if(!stat) goto exception;
			PLCLib::Relay* refRelay=PLCLib::BooleanCalculator::FindRelay(tmpName,this->relay_list,this->nRelay);
			this->output[i]=new PLCLib::Output(tmpName, outport, refRelay);

		}

		relayIndex=nDI;
		for(i=0;i<nDL;i++, relayIndex++)
		{
			PLCLib::DigitalLogicRelay* DL=(PLCLib::DigitalLogicRelay*)this->relay_list[relayIndex];
			PLCLib::BooleanCalculator* pcal=PLCLib::BooleanCalculator::Parse(logics[i],0, bsize, this->relay_list, this->nRelay);
			DL->SetCalculator(pcal);
#ifdef  SERIAL_TRACE_ON

#endif
		}

		buffer=this->full_buffer;

	}

	this->cmdConsole->println(PLC::OUT_OK);
	return;

	//EXCEPTION PROGRESS
exception:
	this->cmdConsole->println(PLC::ERR_FMT_PLC_SET);


}



void PLC::processCommand() {
	if(Serial.available())
	{
		int size=this->cmdConsole->readBytesUntil('\n', this->full_buffer, BUFFER_SIZE);
		this->full_buffer[size]='\0';
#ifdef  SERIAL_TRACE_ON
		Serial.println(this->full_buffer);

#endif
		if(strcmp(full_buffer,PLC::CMD_PLC_SET)==0)
		{
			this->deleteAll();
			this->createRelay();
		}
	}
}
